const {query} = require('../db');
const {message, Priority_Queue} = require('../utils')
const MessageCenter = require('./MessageCenter');

const messageCenter = new MessageCenter();

class Room {
  constructor(roomId) {
    this.users = [];
    this.roomId = roomId;
    this.messageQueue = new Priority_Queue();
    this.lastSendTime = Date.now();
  }

  receiveUser(user) {
    // 把新用户加入用户实例表
    this.saveUser(user);

    // 数据库中记录新加入用户
    this.saveUserDB(user)

    // 广播新用户加入的消息
    this.notify(true, user);

    // 给新用户发送历史命令
    this.sendRecords(user);

    // 给新用户设置事件监听器
    this.initListener(user); 
  }

  sendUser(user) {
    // 删除房间中用户实例
    this.deleteUser(user);
    
    // 删除数据库中用户记录
    this.deleteUserDB(user);
    
    // 广播用户离开消息
    this.notify(false, user);

    // 检查该房间是否为空
    this.checkEmptyOrNot();
  }

  initListener(user) {
    // 设置消息到来事件的监听器
    user.websocket.on('message', async (command) => {
      // 用户输入信息校验
      if(command === '') return ;
      
      try {

        // 数据库中存储该命令
        await this.saveCommandDB(command);
        
        // 将命令暂存房间的消息队列
        this.messageEnqueue(command);

        // 设立缓冲时间，之后向房间内所有人广播消息队列的队首命令
        setTimeout(() => {
          this.broadcastCommand();
        }, 30);
      
      } catch(e) {

        // 发生错误，通知用户
        this.messageFailed(user);
      }
    })

    // 设置用户断开连接事件的监听器
    user.websocket.on('close', () => {
      // 房间送走该用户
      this.sendUser(user);
    })
  }

  saveUserDB({userName}) {
    const sql = `insert into roomUser (roomid, username) values('${this.roomId}', '${userName}')`;
    query(sql);
  }

  async saveCommandDB(command) {
    const sql = `insert into commands (roomId, content) values ('${this.roomId}', '${command}')`;
    await query(sql);
  }

  deleteUserDB({userName}) {
    const sql = `delete from roomUser where roomId='${this.roomId}' and userName='${userName}'`;  
    query(sql);
  }

  async searchCommandsDB() {
    const sql = `select content from commands where roomId='${this.roomId}'`;
    return await query(sql);
  }

  deleteRoomDB() {
    const sql = `delete from room where roomId='${this.roomId}'`;
    query(sql);
  }

  deleteRoomRecordsDB() {
    const sql = `delete from commands where roomId='${this.roomId}'`;
    query(sql);
  }

  saveUser(user) {
    this.users.push(user);
    user.userName = user.params.userName;
  }

  deleteUser(user) {
    messageCenter.clear(this.roomId, user)
    const idx = this.users.findIndex(u => u === user);
    this.users.splice(idx, 1);
  }

  checkEmptyOrNot() {
    if(this.users.length >= 1) return ;
    process.nextTick(() => this.eventEmitter.emit('destoryRoom', this.roomId));

    this.deleteRoomDB();

    this.deleteRoomRecordsDB();
  }

  broadcastCommand() {
    try {
      const item = this.messageQueue.shift();
      if(item.priority < this.lastSendTime) {
        this.dealWithDelayMessage(item);
        return ;
      }
      this.lastSendTime = item.priority;
      delete item.priority;
      this.broadcast(JSON.stringify(item));
    } catch(e) {
      console.error( e);
    } 
  }

  dealWithDelayMessage(item) {
    // notify user that his command timed out
  }

  async sendRecords(user) {
    const commands = await this.searchCommandsDB();
    const messages = commands.reduce((pre, item) => { 
      pre.push(item.content);
      return pre;
    }, []);
    messages.sort((a, b) => {
      const Ta = JSON.parse(a).time;
      const Tb = JSON.parse(b).time;
      return Ta < Tb ? -1 : 1;
    })
    const data = {
      type: 'init',
      messages
    }

    process.nextTick(() => user.websocket.send(message('成功', 200, data)));
  }

  messageFailed(user) {
    user.websocket.send(message('失败', 406, '消息发送失败'))
  }

  messageEnqueue(command) {
    const item = JSON.parse(command);
    item.priority = Date.parse(item.time)
    this.messageQueue.push(item);
  }

  notify(isJoin, {userName}) {
    const data = {
      type: 'room',
      time: new Date(Date.now()).toLocaleString('zh-CN', {hour12: false}),
      user: userName,
      isJoin
    }

    this.broadcast(data);
  }

  broadcast(content) {
    for (const user of this.users) {
      user.websocket.send(message('成功', 200, content));
    }
  }

}

module.exports = Room;